﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Text;

namespace AppendCustomisationService
{
    public partial class FileWatchAndAppend : ServiceBase
    {
        private EventLog evLog = new EventLog(ServiceUniqueName);
        private FileSystemWatcher fsw = new FileSystemWatcher();
        private FileSystemEventHandler ChangeEvent;
        public string WatchFilePath
        {
            get
            {
                string dir = ConfigurationManager.AppSettings["WatchDir"];
                string fileName = ConfigurationManager.AppSettings["WatchFile"];
                string seperator = (dir.LastIndexOf("\\") == dir.Length-1) ? "" : "\\";
                string path = dir + seperator + fileName;
                return path;
            }
        }

        public string AppendFilePath
        {
            get
            {
                return ConfigurationManager.AppSettings["AppendPath"];
            }
        }

        public FileWatchAndAppend()
        {
            evLog.Source = ServiceUniqueName;
            evLog.Log = "Application";
            ((System.ComponentModel.ISupportInitialize)(this.fsw)).EndInit();

            ReviewAppendFile();
            ConfigureFileWatcher();
            InitializeComponent();
        }

        /*protected override void OnStart(string[] args)
        {
            ReviewAppendFile();
        }*/

        protected override void OnStop()
        {
            fsw.Changed -= ChangeEvent;
            ReviewAppendFile(removeAppendedFile: true);
        }

        private void ConfigureFileWatcher()
        {
            ChangeEvent = new FileSystemEventHandler(fsw_Changed);
            fsw.Filter = ConfigurationManager.AppSettings["WatchFile"];
            fsw.Path = ConfigurationManager.AppSettings["WatchDir"];
            fsw.EnableRaisingEvents = true;
            fsw.Changed += ChangeEvent;
        }

        private void fsw_Changed(object sender, FileSystemEventArgs e)
        {
            ReviewAppendFile();
        }

        private void ReviewAppendFile(bool removeAppendedFile = false)
        {
            string watchText = "  ";
            string appendText = " ";
            try
            {
                watchText = getTextFrom(WatchFilePath);
                appendText = getTextFrom(AppendFilePath);
            }
            catch (Exception)
            {
            }
            if (!watchText.Contains(appendText) && !removeAppendedFile)
            {
                evLog.WriteEntry("Appending customisations to file.", EventLogEntryType.Information);
                AppendText(WatchFilePath, appendText);

            }
            else if (removeAppendedFile)
            {
                evLog.WriteEntry("Removing customisations from file.", EventLogEntryType.Information);
                RemoveText(WatchFilePath, appendText);
            }
        }

        /// <summary>
        /// Appends text to the end of a file.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="AppendText"></param>
        private void AppendText(string path, string AppendText)
        {
            fsw.Changed -= ChangeEvent; // prevent infinite loop
            try
            {
                WriteBackup(path, "pre-append");
                var write = new StreamWriter(path, append: true);
                write.Write(AppendText);
                write.Flush();
                write.Close();
                write.Dispose();
            }
            catch (Exception ex)
            {
                byte[] msg = new ASCIIEncoding().GetBytes(ex.ToString());
                evLog.WriteEntry("Failed to write back " + path + ". Backup files have been created.", EventLogEntryType.Error);
            }
            finally
            {
                fsw.Changed += ChangeEvent;
            }
        }

        /// <summary>
        /// Removes the Appended text from the content, and saves the file.
        /// </summary>
        /// <param name="path">Path of the content to parse</param>
        /// <param name="AppendText">Path of the content to be removed</param>
        private void RemoveText(string path, string AppendText)
        {
            fsw.Changed -= ChangeEvent; // prevent infinite loop
            try
            {
                WriteBackup(path, "pre-remove");
                var textToRemoveFrom = getTextFrom(path);
                var newText = textToRemoveFrom.Replace(AppendText, "");
                var write = new StreamWriter(path, append: false);
                write.Write(newText);
                write.Flush();
                write.Close();
                write.Dispose();
            }
            catch (Exception ex)
            {
                evLog.WriteEntry("Failed to remove customisations from " + path + ". Backup files have been created. \n"+ex.ToString(), EventLogEntryType.Error);
            }
            finally
            {
                fsw.Changed += ChangeEvent;
            }
        }

        /// <summary>
        /// Writes a Backup of the file at the path specified. Appends a datestamp, a note (nullable), and a new extension. Wrap me in a try method!
        /// </summary>
        /// <param name="path">File to backup</param>
        /// <param name="note">Note to add to file</param>
        private void WriteBackup(string path, string note)
        {
            var myNote = (String.IsNullOrWhiteSpace(note)) ? "" : "." + note + "";
            string newFileName = path + DateTime.Now.ToFileTimeUtc() + myNote + ".bak";
            string backup = getTextFrom(path);
            var backupWrite = new StreamWriter(newFileName, append: false);
            backupWrite.Write(backup);
            backupWrite.Flush();
            backupWrite.Close();
            backupWrite.Dispose();
        }

        /// <summary>
        /// Gets text from the path provided. Wrap me in a try method!
        /// </summary>
        /// <param name="path">Complete path</param>
        /// <returns>All text</returns>
        private string getTextFrom(string path)
        {
            try
            {
                var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read));
                string AppendText = reader.ReadToEnd();
                reader.Close();
                reader.Dispose();
                return AppendText;
            }
            catch (Exception ex)
            {   
                evLog.WriteEntry("Error reading file " + path + "\n" + ex.ToString(), EventLogEntryType.Information);
                throw ex;
            }
        }
    }
}